package com.lagou.rpc.consumer.proxy;

import com.alibaba.fastjson.JSON;
import com.lagou.rpc.common.RpcRequest;
import com.lagou.rpc.common.RpcResponse;
import com.lagou.rpc.consumer.client.RpcClient;
import com.lagou.rpc.util.ZkUtil;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.SecureRandom;
import java.util.*;

/**
 * 客户端代理类-创建代理对象
 * 1.封装request请求对象
 * 2.创建RpcClient对象
 * 3.发送消息
 * 4.返回结果
 */
@Service
public class RpcClientProxy {

    Map<String, String[]> connectMap = new HashMap<>();


    // 监听服务
    public void watchService(){

        try {
            TreeCache treeCache  =
                    new TreeCache(ZkUtil.getZkClient(), "/lg-rpc");
            TreeCacheListener treeCacheListener =
                    new TreeCacheListener() {
                        @Override
                        public void childEvent(CuratorFramework client,
                                               TreeCacheEvent event) throws Exception {
                            ChildData data = event.getData();
                            if(data==null || data.getPath().equals("/lg-rpc"))
                            {
                                return;
                            }

                            String nodeData = new String(data.getData());

                            switch (event.getType()) {
                                case NODE_ADDED:
                                    System.out.println("节点增加，path：" + data.getPath() + "，节点内容：" + nodeData);

                                    String[] ipAndPort = nodeData.split(":", -1);
                                    connectMap.put(data.getPath(), ipAndPort);
                                    break;
                                case NODE_REMOVED:
                                    System.out.println("节点删除，path：" + data.getPath() + "，节点内容：" + nodeData);

                                    connectMap.remove(data.getPath());

                                    break;
                                default:
                                    break;
                            }

                        }
                    };
            treeCache.getListenable().addListener(treeCacheListener);
            treeCache.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object createProxy(Class serviceClass) {

        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{serviceClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //1.封装request请求对象
                        RpcRequest rpcRequest = new RpcRequest();
                        rpcRequest.setRequestId(UUID.randomUUID().toString());
                        rpcRequest.setClassName(method.getDeclaringClass().getName());
                        rpcRequest.setMethodName(method.getName());
                        rpcRequest.setParameterTypes(method.getParameterTypes());
                        rpcRequest.setParameters(args);
                        //2.创建RpcClient对象
                        String address = loanBalance();

                        String[] ipAndPort = address.split(":", -1);

                        RpcClient rpcClient = new RpcClient(ipAndPort[0], Integer.valueOf(ipAndPort[1]));
                        try {

                            // 请求时间
                            long reqTime = System.currentTimeMillis();

                            //3.发送消息
                            Object responseMsg = rpcClient.send(JSON.toJSONString(rpcRequest));

                            // 更新响应时间
                            long respTime = System.currentTimeMillis();
                            // 响应时间
                            long serviceTime = respTime - reqTime;
                            // 更新节点响应时间
                            updateNodeData(address, reqTime + ":" + serviceTime);

                            RpcResponse rpcResponse = JSON.parseObject(responseMsg.toString(), RpcResponse.class);
                            if (rpcResponse.getError() != null) {
                                throw new RuntimeException(rpcResponse.getError());
                            }
                            //4.返回结果
                            Object result = rpcResponse.getResult();
                            return JSON.parseObject(result.toString(), method.getReturnType());
                        } catch (Exception e) {
                            throw e;
                        } finally {
                            rpcClient.close();
                        }

                    }

                });
    }

    // 负载均衡
    private static String loanBalance() throws Exception {

        ZkUtil zkUtil = new ZkUtil();

        Map<String, String> nodeInfoMap = zkUtil.getAddressList();

        int minTime = 5;
        List<String> addressList = new ArrayList<>();
        for (Map.Entry<String, String> nodeEntry : nodeInfoMap.entrySet()){

            // 节点内容
            String nodeData = nodeEntry.getValue();

            // 响应时间
            int serviceTime = Integer.valueOf(nodeData.split(":", -1)[1]);

            System.out.println("节点" + nodeEntry.getKey() + "响应时间：" + serviceTime);

            if (serviceTime == minTime)
            {
                addressList.add(nodeEntry.getKey());
            }
            else if (serviceTime < minTime)
            {
                addressList = new ArrayList<>();
                addressList.add(nodeEntry.getKey());
                minTime = serviceTime;
            }
        }

        int i = 0;
        if (addressList.size() > 1)
        {
            i = new Random().nextInt(addressList.size());
            System.out.println("响应时间最短，取随机数，i = " + i);
        }

        String minNode = addressList.get(i);

        System.out.println("节点" + minNode + "响应时间最短：" + minTime);


        return minNode;
    }

    // 更新节点内容
    private void updateNodeData(String address, String nodeData) throws Exception {

        ZkUtil zkUtil = new ZkUtil();
        zkUtil.updateNodeData(address, nodeData);
    }
}
